﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using HomeFramework.Core;
using System.IO;
using System.Reflection;
using System.ServiceModel;
using HomeFramework.Model;
using System.Diagnostics;
using System.ServiceModel.Channels;
using System.Xml;
using System.Runtime.Caching;
using TNX.Common.Legacy;

namespace HomeFramework.Services
{
    static partial class HomeFrameworkEngine
    {
        [Serializable]
        internal struct WsdlGeneratorRequest
        {
            public static readonly object SyncFlag = new object();
            public string handlerFullName;
            public string contractFullName;
            public string baseAddress;
            public string assemblyName;
            public byte[][] references;
            public byte[] assembly;
        }

        [Serializable]
        internal struct WsdlGeneratorResponse
        {
            public byte[] Wsdl;
        }

        internal class WsdlGenerator
        {
            private readonly static TNX.Common.CacheManager WsdlCache = new TNX.Common.CacheManager();
            public static Message GenerateWsdl(string name, string version, string baseAddress)
            {
                Log.WriteLine(Log.SourceEnum.WsdlGenerator, TraceEventType.Verbose, "Wsdl listener working for {0}", name);
                DateTime start = DateTime.Now;

                var context = WsdlCache.GetOrDrillShortSliding(() =>
                    new AppDomainContextSmartCached<WsdlGeneratorRequest, WsdlGeneratorResponse>(), name, version, baseAddress);

                using (var ts = new TransactionScript())
                {
                    var handler = ts.FindByName(name, version);
                    if (handler == null)
                        return null;

                    var response = context.ExecuteCrossDomainOperation(request =>
                        {
                            Assembly assembly;
                            lock (WsdlGeneratorRequest.SyncFlag)
                            {
                                assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName == request.assemblyName);
                                if (assembly == null)
                                {
                                    foreach (var b in request.references)
                                        Assembly.Load(b);

                                    assembly = Assembly.Load(request.assembly);
                                }
                            }

                            using (var m = new MemoryStream())
                            {
                                var t = assembly.GetType(request.contractFullName);
                                var contract = t.GetCustomAttributes(typeof(ServiceContractAttribute), true).Cast<ServiceContractAttribute>().Single();

                                using (ServiceHost h = new ServiceHost(assembly.GetType(request.handlerFullName)))
                                {
                                    var e = getHomeFrameworkEndpoints().FirstOrDefault();
                                    if (e == null)
                                        throw new ArgumentException("No endpoints to build wsdl!");

                                    var address = string.Format("{0}?name={1}&version={2}", request.baseAddress, (string.IsNullOrEmpty(contract.Name) ? t.Name : contract.Name), t.Assembly.GetName().Version.ToString());
                                    var endpoint = h.AddServiceEndpoint(t, e.Binding, address);

                                    HomeFramework.Core.FlatWsdlBuilder.GenerateWsdl(m, endpoint);
                                    return new WsdlGeneratorResponse { Wsdl = m.ToArray() };
                                }
                            }
                        },
                        new WsdlGeneratorRequest
                        {
                            handlerFullName = handler.HandlerFullName,
                            contractFullName = handler.ContractFullName,
                            baseAddress = baseAddress,
                            assemblyName = handler.AssemblyFullName,
                            references = handler.References.Select(x => x.ReferenceAssembly).ToArray(),
                            assembly = handler.AssemblyData,
                        });

                    Log.WriteLine(Log.SourceEnum.WsdlGenerator, TraceEventType.Verbose, "Wsdl listener worked for {0} in {1:N0}ms", name, (DateTime.Now - start).TotalMilliseconds.ToString());
                    var r = XmlReader.Create((new MemoryStream(response.Wsdl)));
                    var risp = NewEnvelope(r);
                    return risp;
                }
            }
        }
    }
}